/*
 * Copyright Hundsun Technologies Inc. All Rights Reserved.
 */

package module

import (
	"fmt"
	"strings"
	"sync"

	"hundsun.com/hsl/hschain/common/types"

	"hundsun.com/hsl/hschain/blockchain"
	"hundsun.com/hsl/hschain/common/config"
	"hundsun.com/hsl/hschain/common/log/logging"

	consensus "hundsun.com/hsl/hschain/consensus/common"

	"hundsun.com/hsl/hschain/executor/worker"
	"hundsun.com/hsl/hschain/module"
	"hundsun.com/hsl/hschain/module/common/cache"
	"hundsun.com/hsl/hschain/module/common/utils"
	"hundsun.com/hsl/hschain/p2p"
	"hundsun.com/hsl/hschain/rpc"
	"hundsun.com/hsl/hschain/store"
)

const (
	configKeyInternalIP = "console.internalIP"
	configKeyPassword   = "console.password"
	configKeyPort       = "console.port"
	configKeyUsername   = "console.username"
)

//LocalManagementService ...
type LocalManagementService struct {
	moduleCache       *cache.ModuleCache
	moduleNotifyCache *cache.ModuleNotifyCache
}

var mgtService *LocalManagementService
var once sync.Once
var log logging.Log

//GetMgtServiceInstance 获取实例
func GetMgtServiceInstance() *LocalManagementService {
	once.Do(func() {
		mgtService = &LocalManagementService{}
		mgtService.moduleCache = cache.GetModuleCacheInstance()
		mgtService.moduleNotifyCache = cache.GetModuleNotifyCacheInstance()

		log = logging.GetLogWithCommonConf(logging.ConfigKeyConsoleLogLevel, logging.LogFieldModuleMgt, logging.LogFieldModuleMgt)
	})
	return mgtService
}

var consoleConf *config.ConsoleConfig
var consoleConfigOnce sync.Once

//GetCommonConfig 获取common config
func GetCommonConfig() *config.ConsoleConfig {
	consoleConfigOnce.Do(func() {
		consoleConf = &config.ConsoleConfig{
			InternalIP: config.GetStringConfigValue(configKeyInternalIP, "localhost"),
			Username:   config.GetStringConfigValue(configKeyUsername, ""),
			Password:   config.GetStringConfigValue(configKeyPassword, ""),
			Port:       config.GetIntConfigValue(configKeyPort, 8080),
		}
		if strings.TrimSpace(consoleConf.InternalIP) == "" {
			log.Fatal("host internal ip is empty")
		}
	})
	return consoleConf
}

//Init 初始化
func (mgtService *LocalManagementService) Init() error {
	log.Info("module management service initiating ...")
	// init variables
	_ = GetCommonConfig()
	log.Infof("host internal ip: %s", consoleConf.InternalIP)

	fileName := "module_configs.yaml"
	filePath, err := utils.GetFilePath(fileName, log)
	if err != nil {
		log.Errorf("Get config file path failed, err: %s\n", err.Error())
		return err
	}

	configs, err := utils.ParseConfigs(filePath, log)
	if err != nil {
		log.Errorf("Parse config from path %s failed, err: %s\n", filePath, err.Error())
		return err
	}

	// startup modules
	for _, moduleConfig := range configs.Modules {
		log.Infof("module - [Startup] moduleID:%s version:%d", moduleConfig.ModuleID, moduleConfig.Version)
		// startup a module
		go mgtService.startupModule(moduleConfig)
	}

	// module check 目前是loaclmq,因此所有模块需要检查是否配置，
	// 后期分布式部署模块需要一个管理模块定时进行检查
	cms := make(map[string]struct{})
	for _, cm := range configs.Modules {
		cms[cm.ModuleName] = struct{}{}
	}
	needModules := module.GetAllModule()
	for _, nm := range needModules {
		if _, ok := cms[nm]; !ok {
			panic(fmt.Sprintf("not find module %s config", nm))
		}
	}

	return nil
}

//Close close
func (mgtService *LocalManagementService) Close() {
	modCache := cache.GetModulePointerCacheInstance()
	mods := modCache.GetAll()
	for _, mod := range mods {
		if mod.ModulePointer != nil {
			m := *mod.ModulePointer
			m.Shutdown()
		}
	}
	// 后端存储关闭
	store.Close()
}

func (mgtService *LocalManagementService) startupModule(config *config.ModuleConfig) (module.Status, error) {
	var status module.Status

	log.Infof("startupModule  name: %s", config.ModuleName)
	// new module
	m, err := newModule(*config)
	if err != nil {
		log.Errorf("failed to new module '%s' with err: %s", config.ModuleName, err.Error())
		status = module.StatusError
		return status, err
	}

	mgtService.moduleNotifyCache.AddWithInterestedModules(m.Name(), m.GetInterestedModules())

	// startup
	status, err = m.Startup(config)
	if status != module.StatusRunning {
		return status, err
	}

	// 先缓存信息，再在注册中心注册
	cacheConfig, found := mgtService.moduleCache.Get(config.ModuleID)
	if !found {
		// set cache module info
		cacheConfig = &cache.ModuleCacheConfig{}
		cacheConfig.Init(config)
		cacheConfig.IsInited = true
		mgtService.moduleCache.Set(config.ModuleID, cacheConfig)
		log.Infof("module config cache - [Create] moduleID:%s status:'%s' version:%d", config.ModuleID, status, config.Version)
	} else {
		// update cache module info
		cacheConfig.IsInited = true
		log.Infof("module config cache - [Update] moduleID:'%s' status:'%s'", config.ModuleID, status)
	}

	// registry module
	var leaseID int64

	var regKey = ""
	r, err := m.GetRegisteredKV()
	if err != nil {
		return module.StatusError, err
	}
	regKey = r.Key.String()

	// save module reference
	pointerCache := cache.GetModulePointerCacheInstance()
	pointer := &cache.ModuleCachePointer{}
	pointer.ModuleID = config.ModuleID
	pointer.IsMasterStandbyMode = m.IsMasterStandbyMode()
	pointer.RegistryKey = regKey
	pointer.LeaseID = leaseID
	pointer.ModulePointer = &m
	pointerCache.Set(config.ModuleID, pointer)
	log.Infof("module pointer cache - [Create] moduleID:'%s' status:'%s', leaseID:'%d'", config.ModuleID, status, leaseID)

	return status, nil
}

func newModule(config config.ModuleConfig) (module.Module, error) {
	var m module.Module
	var moduleName = config.ModuleName
	nameEnum, found := module.NewNameEnum(moduleName)
	if !found {
		log.Errorf("module name '%s' is wrong.", moduleName)
		return nil, types.ErrNotFound
	}
	log.Infof("new module instance: %s", config.ModuleID)

	switch nameEnum {
	case module.Consensus:
		m = consensus.NewGenesisConsensusModule()
	case module.Blockchain:
		m = blockchain.NewBlockChainModule()
	case module.WorkerExecutor:
		m = worker.NewWorkerModule()
	case module.P2p:
		m = p2p.NewP2pModule()
	case module.RPC:
		m = rpc.NewRPCModule()
	default:
		return nil, types.ErrNotFound
	}

	return m, nil
}

/*
func convertConfigSet2Map(confSet *config.HostModulesConfigSet) (map[string]interface{}, error) {
	var configMap = make(map[string]interface{})
	for _, hostConfig := range *confSet {
		targetHost := hostConfig.TargetHost
		for _, m := range hostConfig.Modules {
			moduleConfig := &config.ModuleConfig{}
			moduleConfig.TargetHost = targetHost
			_, found := module.NewNameEnum(m.ModuleName)
			if !found {
				log.Errorf("module name '%s' is wrong.", m.ModuleName)
				// 入参所有配置项需均无错误
				return nil, ErrNotFoundModuleName
			}
			moduleConfig.ModuleName = m.ModuleName
			moduleConfig.Version = int32(time.Now().Unix())
			port := utils.GetPort(m.Parameters)
			moduleConfig.Parameters = m.Parameters
			moduleConfig.ModuleID = fmt.Sprintf("/%s/%s:%s", moduleConfig.ModuleName, targetHost, port)

			if _, ok := configMap[moduleConfig.ModuleID]; ok {
				// 入参重复配置项将被忽略（依据ModuleID）
				continue
			}

			configMap[moduleConfig.ModuleID] = moduleConfig
		}
	}
	return configMap, nil
}*/
